/***************************************************************************/
/*                                                                         */
/*  otvmod.c                                                               */
/*                                                                         */
/*    FreeType's OpenType validation module implementation (body).         */
/*                                                                         */
/*  Copyright 2004, 2005, 2006, 2007, 2008 by                              */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#include <ft2build.h>
#include FT_TRUETYPE_TABLES_H
#include FT_TRUETYPE_TAGS_H
#include FT_OPENTYPE_VALIDATE_H
#include FT_INTERNAL_OBJECTS_H
#include FT_SERVICE_OPENTYPE_VALIDATE_H

#include "otvmod.h"
#include "otvalid.h"
#include "otvcommn.h"


/*************************************************************************/
/*                                                                       */
/* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
/* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
/* messages during execution.                                            */
/*                                                                       */
#undef  FT_COMPONENT
#define FT_COMPONENT trace_otvmodule


static FT_Error
otv_load_table(FT_Face face,
               FT_Tag tag,
               FT_Byte* volatile* table,
               FT_ULong* table_len)
{
    FT_Error error;
    FT_Memory memory = FT_FACE_MEMORY(face);


    error = FT_Load_Sfnt_Table(face, tag, 0, NULL, table_len);
    if (error == OTV_Err_Table_Missing)
        return OTV_Err_Ok;
    if (error)
        goto Exit;

    if (FT_ALLOC(*table, *table_len))
        goto Exit;

    error = FT_Load_Sfnt_Table(face, tag, 0, *table, table_len);

Exit:
    return error;
}


static FT_Error
otv_validate(FT_Face volatile face,
             FT_UInt ot_flags,
             FT_Bytes* ot_base,
             FT_Bytes* ot_gdef,
             FT_Bytes* ot_gpos,
             FT_Bytes* ot_gsub,
             FT_Bytes* ot_jstf)
{
    FT_Error error = OTV_Err_Ok;
    FT_Byte* volatile base;
    FT_Byte* volatile gdef;
    FT_Byte* volatile gpos;
    FT_Byte* volatile gsub;
    FT_Byte* volatile jstf;
    FT_Byte* volatile math;
    FT_ULong len_base, len_gdef, len_gpos, len_gsub, len_jstf;
    FT_ULong len_math;
    FT_UInt num_glyphs = (FT_UInt)face->num_glyphs;
    FT_ValidatorRec volatile valid;


    base = gdef = gpos = gsub = jstf = math = NULL;
    len_base = len_gdef = len_gpos = len_gsub = len_jstf = len_math = 0;

    /*
     * XXX: OpenType tables cannot handle 32-bit glyph index,
     *      although broken TrueType can have 32-bit glyph index.
     */
    if (face->num_glyphs > 0xFFFFL)
    {
        FT_TRACE1(("otv_validate: Invalid glyphs index (0x0000FFFF - 0x%08x) ",
                   face->num_glyphs));
        FT_TRACE1(("are not handled by OpenType tables\n"));
        num_glyphs = 0xFFFF;
    }

    /* load tables */

    if (ot_flags & FT_VALIDATE_BASE)
    {
        error = otv_load_table(face, TTAG_BASE, &base, &len_base);
        if (error)
            goto Exit;
    }

    if (ot_flags & FT_VALIDATE_GDEF)
    {
        error = otv_load_table(face, TTAG_GDEF, &gdef, &len_gdef);
        if (error)
            goto Exit;
    }

    if (ot_flags & FT_VALIDATE_GPOS)
    {
        error = otv_load_table(face, TTAG_GPOS, &gpos, &len_gpos);
        if (error)
            goto Exit;
    }

    if (ot_flags & FT_VALIDATE_GSUB)
    {
        error = otv_load_table(face, TTAG_GSUB, &gsub, &len_gsub);
        if (error)
            goto Exit;
    }

    if (ot_flags & FT_VALIDATE_JSTF)
    {
        error = otv_load_table(face, TTAG_JSTF, &jstf, &len_jstf);
        if (error)
            goto Exit;
    }

    if (ot_flags & FT_VALIDATE_MATH)
    {
        error = otv_load_table(face, TTAG_MATH, &math, &len_math);
        if (error)
            goto Exit;
    }

    /* validate tables */

    if (base)
    {
        ft_validator_init(&valid, base, base + len_base, FT_VALIDATE_DEFAULT);
        if (ft_setjmp(valid.jump_buffer) == 0)
            otv_BASE_validate(base, &valid);
        error = valid.error;
        if (error)
            goto Exit;
    }

    if (gpos)
    {
        ft_validator_init(&valid, gpos, gpos + len_gpos, FT_VALIDATE_DEFAULT);
        if (ft_setjmp(valid.jump_buffer) == 0)
            otv_GPOS_validate(gpos, num_glyphs, &valid);
        error = valid.error;
        if (error)
            goto Exit;
    }

    if (gsub)
    {
        ft_validator_init(&valid, gsub, gsub + len_gsub, FT_VALIDATE_DEFAULT);
        if (ft_setjmp(valid.jump_buffer) == 0)
            otv_GSUB_validate(gsub, num_glyphs, &valid);
        error = valid.error;
        if (error)
            goto Exit;
    }

    if (gdef)
    {
        ft_validator_init(&valid, gdef, gdef + len_gdef, FT_VALIDATE_DEFAULT);
        if (ft_setjmp(valid.jump_buffer) == 0)
            otv_GDEF_validate(gdef, gsub, gpos, num_glyphs, &valid);
        error = valid.error;
        if (error)
            goto Exit;
    }

    if (jstf)
    {
        ft_validator_init(&valid, jstf, jstf + len_jstf, FT_VALIDATE_DEFAULT);
        if (ft_setjmp(valid.jump_buffer) == 0)
            otv_JSTF_validate(jstf, gsub, gpos, num_glyphs, &valid);
        error = valid.error;
        if (error)
            goto Exit;
    }

    if (math)
    {
        ft_validator_init(&valid, math, math + len_math, FT_VALIDATE_DEFAULT);
        if (ft_setjmp(valid.jump_buffer) == 0)
            otv_MATH_validate(math, num_glyphs, &valid);
        error = valid.error;
        if (error)
            goto Exit;
    }

    *ot_base = (FT_Bytes)base;
    *ot_gdef = (FT_Bytes)gdef;
    *ot_gpos = (FT_Bytes)gpos;
    *ot_gsub = (FT_Bytes)gsub;
    *ot_jstf = (FT_Bytes)jstf;

Exit:
    if (error)
    {
        FT_Memory memory = FT_FACE_MEMORY(face);


        FT_FREE(base);
        FT_FREE(gdef);
        FT_FREE(gpos);
        FT_FREE(gsub);
        FT_FREE(jstf);
    }
    {
        FT_Memory memory = FT_FACE_MEMORY(face);


        FT_FREE(math);                 /* Can't return this as API is frozen */
    }

    return error;
}


static
const FT_Service_OTvalidateRec otvalid_interface =
{
    otv_validate
};


static
const FT_ServiceDescRec otvalid_services[] =
{
    { FT_SERVICE_ID_OPENTYPE_VALIDATE, &otvalid_interface },
    { NULL, NULL }
};


static FT_Pointer
otvalid_get_service(FT_Module module,
                    const char* service_id)
{
    FT_UNUSED(module);

    return ft_service_list_lookup(otvalid_services, service_id);
}


FT_CALLBACK_TABLE_DEF
const FT_Module_Class otv_module_class =
{
    0,
    sizeof(FT_ModuleRec),
    "otvalid",
    0x10000L,
    0x20000L,

    0,              /* module-specific interface */

    (FT_Module_Constructor)0,
    (FT_Module_Destructor)0,
    (FT_Module_Requester)otvalid_get_service
};


/* END */